﻿using System;
using System.Diagnostics;
using System.IO;
using SfsTools.Core;
using mindvnas.Common;

namespace SfsTools.Format
{
    /// <summary>
    /// Implements the <em>format</em> application.
    /// </summary>
    public class Program : ConsoleBase
    {
        #region Properties

        /// <summary>
        /// Contains the path to the image file to create.
        /// </summary>
        private String FImageFile;
        /// <summary>
        /// Contains the target image size.
        /// </summary>
        private Int64 FImageSize;
        /// <summary>
        /// Contains the target block size.
        /// </summary>
        private Int32 FBlockSize;

        #endregion

        #region Methods

        #region Entry point
        /// <summary>
        /// Entry point.
        /// </summary>
        /// <param name="arguments"></param>
        static void Main(String[] arguments)
        {
            new Program().Process(arguments);
        }
        #endregion

        #region Arguments

        /// <summary>
        /// Invoked before processing arguments.
        /// </summary>
        protected override void OnBeforeProcessArguments()
        {
            FImageSize = 1440 * 1024; // assume 1.44 floppy
            FBlockSize = 512; // assume 512 byte sectors.
        }

        /// <summary>
        /// Processes the specified command-line argument.
        /// </summary>
        /// <param name="argument">The command-line argument.</param>
        protected override void ProcessArgument(ConsoleBaseArgument argument)
        {
            if (argument.Name == "-image")
            {
                FImageFile = argument.Value;
                Arguments.Add(argument);
            }
            else if (argument.Name == "-size")
            {
                long multiplier = 1;
                String size = argument.Value.ToUpperInvariant();
                if (size.EndsWith("K"))
                {
                    size = size.Substring(0, size.Length - 1);
                    multiplier = 1024;
                }
                else if (size.EndsWith("M"))
                {
                    size = size.Substring(0, size.Length - 1);
                    multiplier = 1024 * 1024;
                }
                else if (size.EndsWith("G"))
                {
                    size = size.Substring(0, size.Length - 1);
                    multiplier = 1024 * 1024 * 1024;
                }
                try
                {
                    FImageSize = Int64.Parse(size) * multiplier;
                }
                catch (FormatException)
                {
                    WriteLine(Translation.Resources.GetString("InvI"), argument.Value);
                }
            }
            else if (argument.Name == "-block")
            {
                int multiplier = 1;
                String size = argument.Value.ToUpperInvariant();
                if (size.EndsWith("K"))
                {
                    size = size.Substring(0, size.Length - 1);
                    multiplier = 1024;
                }
                try
                {
                    int blockSize = Int32.Parse(size) * multiplier;
                    BootBlock bb = new BootBlock();
                    try
                    {
                        bb.BlockSize = blockSize; // force unacceptable values to throw an exception.
                        FBlockSize = bb.BlockSize;
                    }
                    catch (ArgumentException)
                    {
                        WriteLine(Translation.Resources.GetString("InvB1"), argument.Value);
                    }
                }
                catch (FormatException)
                {
                    WriteLine(Translation.Resources.GetString("InvB2"), argument.Value);
                }
            }
            else
            {
                base.ProcessArgument(argument);
            }
        }

        /// <summary>
        /// Displays an usage notice.
        /// </summary>
        [DebuggerStepThrough]
        protected override void UsageParameters()
        {
            base.UsageParameters();
            //
            UsageParameter("-image", Translation.Resources.GetString("ArgI"));
            UsageParameter("-size", Translation.Resources.GetString("ArgS"));
            UsageParameter("-block", Translation.Resources.GetString("ArgB"));
        }

        #endregion

        /// <summary>
        /// Performs whatever the application does.
        /// </summary>
        protected override void ProcessCore()
        {
#if DEBUG
            String dir = @"E:\Work\VisualStudio\SfsTools\Core\";
            FImageFile = dir + "disk";
            FImageSize = 16 * 1024;
#endif
            if (HelpRequested || FImageFile == null)
            {
                HelpRequested = true;
                base.ProcessCore();
            }
            else
            {
                FileBackedVolume volume ;
                Write(Translation.Resources.GetString(new String('X', 1)));
                try
                {
                    volume = FileBackedVolume.Create(FImageFile, FImageSize);
                    Write(Translation.Resources.GetString(new String('Y', 1)));
                }
                finally
                {
                    WriteLine();
                }
                using (volume)
                {
                    FileSystemCreationParameters fscp = new FileSystemCreationParameters();
                    fscp.BlockSize = FBlockSize;
                    fscp.VolumeSize = FImageSize;
                    using (FileSystem.Create(volume, fscp))
                    {
                        // Done.
                        long unused = fscp.VolumeSize - fscp.ReservedAreaSize - fscp.DataAreaSize - fscp.IndexAreaSize;
                        WriteLine(Translation.Resources.GetString(new String('A', 1)),
                            Display(fscp.VolumeSize));
                        WriteLine(Translation.Resources.GetString(new String('B', 1)),
                            fscp.VolumeSize / fscp.BlockSize, fscp.BlockSize);
                        WriteLine(Translation.Resources.GetString(new String('C', 1)),
                            fscp.ReservedAreaSize / fscp.BlockSize, Display(fscp.ReservedAreaSize));
                        WriteLine(Translation.Resources.GetString(new String('D', 1)),
                            fscp.DataAreaSize / fscp.BlockSize, Display(fscp.DataAreaSize));
                        WriteLine(Translation.Resources.GetString(new String('E', 1)), 
                            fscp.IndexAreaSize / fscp.BlockSize, Display(fscp.IndexAreaSize),
                            ((float)fscp.IndexAreaSize / (fscp.IndexAreaSize + fscp.DataAreaSize + unused) * 100.0).ToString("0.00"));
                        WriteLine(Translation.Resources.GetString(new String('F', 1)),
                            unused / fscp.BlockSize, Display(unused));
#if DEBUG
                        Console.ReadLine();
#endif
                    }
                }
            }
        }

        [DebuggerStepThrough]
        private static String Display(long value)
        {
            if (value < 1024)
                return String.Format("{0} B", value);
            if (value < 1024*1024)
                return String.Format("{0} KiB", value / 1024);
            if (value < 1024 * 1024 * 1024)
                return String.Format("{0} MiB", (value / (1024 * 1024.0)).ToString("0.00"));
            return String.Format("{0} GiB", (value / (1024 * 1024 * 1024.0)).ToString("0.00"));
        }

        #endregion

        #region Constructors
        /// <overloads>Initializes a new instance of <see cref="Program"/>.</overloads>
        /// <summary>
        /// Initializes a new instance of <see cref="Program"/>.
        /// </summary>
        [DebuggerStepThrough]
        public Program()
            : base()
        {
        }
        #endregion
    }
}
